package nl.rug.ml.dobbelen.agent;

import java.util.LinkedList;
import java.util.List;

import nl.rug.ml.dobbelen.game.Dice;
import nl.rug.ml.dobbelen.game.DobbelenCondition;
import nl.rug.ml.dobbelen.game.DobbelenDiceScorer;
import nl.rug.ml.dobbelen.game.FairDice;

/**
 * @author Do Bich Ngoc (bichngocdo89@gmail.com)
 * 
 */
public abstract class DobbelenAgent {

	protected int totalScore;
	protected int currentScore;

	protected List<Dice> dices;
	protected int turn;

	protected int numRow;

	public DobbelenAgent(int numDice) {
		totalScore = 0;
		currentScore = 0;
		numRow = 0;
		dices = new LinkedList<Dice>();
		for (int i = 0; i < numDice; i++) {
			dices.add(new FairDice());
		}
	}

	public int getTotalScore() {
		return totalScore;
	}

	public int getCurrentScore() {
		return currentScore;
	}

	public List<Dice> getDices() {
		return dices;
	}

	public int getNumRow() {
		return numRow;
	}

	public void printDices() {
		StringBuilder sb = new StringBuilder();
		for (Dice dice : dices) {
			sb.append(dice.toString() + " ");
		}
		System.out.println(sb.toString());
	}

	public void printTrace() {
		printDices();
		System.out.printf("Score: %d Total: %d\n", getCurrentScore(), getTotalScore());
	}

	/**
	 * The first throw.
	 * 
	 * @throws Exception
	 */
	public void throwDices() {
		numRow++;
		for (Dice dice : dices) {
			dice.reset();
			dice.throwIt(1);
			turn = 1;
		}
		currentScore = DobbelenDiceScorer.calculateScore(dices);
	}

	public boolean mustRethrow() {
		return currentScore < 350;
	}

	/**
	 * Re-throw, but hold some dices.
	 * 
	 * @param heldDices
	 * @return true if the action is successful
	 */
	public boolean reThrowDices(List<Dice> heldDices) {
		if (DobbelenCondition.mustThrowAll(dices) && !heldDices.isEmpty()) {
			System.err.println("Must throw all");
			return false;
		}

		turn++;
		for (Dice dice : dices) {
			if (!heldDices.contains(dice)) {
				dice.throwIt(turn);
			}
		}

		if (DobbelenCondition.newDicesYeldNoScore(dices, heldDices)) {
			currentScore = 0;
			return false;
		}
		else {
			currentScore = DobbelenDiceScorer.calculateScore(dices);
			return true;
		}

	}

	public void updateScore() {
		if (currentScore >= 350) {
			totalScore += currentScore;
		}
	}

	public void reset() {
		currentScore = 0;
		totalScore = 0;
		numRow = 0;
	}

	/**
	 * Determine to re-throw or not.
	 * 
	 * @return
	 */
	public abstract boolean willReThrow();

	/**
	 * Choose dices to be held in new throw.
	 * 
	 * @return the holding dices
	 */
	public abstract List<Dice> chooseDicesToHold();

}
